﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Globalization;
using System.Reflection;

namespace WFA
{
    public partial class Form1 : Form
    {
        private FuzzyController _controller;
        public Form1()
        {
            InitializeComponent();

            #region Dodajemy funkcje do listy rozwijanej
            functionBox.Items.Add(new FunctionItem
                {
                    Function = Functions.F1,
                    Name = "(10.3) F1(x,y) = (1+1/x^ ...",
                    Start = 1f,
                    End = 5f
                });

            functionBox.Items.Add(new FunctionItem
            {
                Function = Functions.F2,
                Name = "(10.4) F2(x,y) = 0.1x+0.05...",
                Start = -4f,
                End = 6f
            });
            #endregion

            // tworzymy kontroler
            _controller = new FuzzyController(Functions.F1, xStart: -4, xEnd: 6);
            functionBox.SelectedIndex = 0;

            //float step = (_controller.EndAt-_controller.StartAt) / 30f;

            //// Learn
            //for (float x1 = _controller.StartAt; x1 <= _controller.EndAt; x1 += step)
            //{
            //    for (float x2 = _controller.StartAt; x2 <= _controller.EndAt; x2 += step)
            //    {
            //        _controller.Learn(x1, x2);
            //    }
            //}

            ////step /= 2f;

            //// Unsharp
            //step += 0.5f;
            //Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
            //using (StreamWriter sw = new StreamWriter(File.Open("./out.dat", FileMode.Create, FileAccess.Write)))
            //{
            //    sw.WriteLine("X Y Z");
            //    for (float x1 = _controller.StartAt; x1 <= _controller.EndAt; x1 += step)
            //    {
            //        for (float x2 = _controller.StartAt; x2 <= _controller.EndAt; x2 += step)
            //        {
            //            float y = _controller.Sharpen(x1, x2);

            //            sw.WriteLine("{0} {1} {2}", x1, x2, y);

            //        }
            //    }
            //}

            //using (StreamWriter sw = new StreamWriter(File.Open("./matrix.dat", FileMode.Create, FileAccess.Write)))
            //{


            //    string l = "0 ";
            //    for (float x1 = _controller.StartAt; x1 <= _controller.EndAt; x1 += step)
            //    {
            //        l += x1 + " ";
            //    }

            //    sw.WriteLine(l);

            //    for (float x1 = _controller.StartAt; x1 <= _controller.EndAt; x1 += step)
            //    {
            //        l = x1 + " ";
            //        for (float x2 = _controller.StartAt; x2 <= _controller.EndAt; x2 += step)
            //        {
            //            float y = _controller.Sharpen(x1, x2);

            //            l += y + " ";
            //        }
            //        sw.WriteLine(l);
            //    }
            //}

            sharpeningFunctions.SelectedIndex = 0;
            showGraph.Checked = true;
        }

        private void functionBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            _controller.Function = (functionBox.SelectedItem as FunctionItem).Function;
            _controller.StartAt = (functionBox.SelectedItem as FunctionItem).Start;
            _controller.EndAt = (functionBox.SelectedItem as FunctionItem).End;
        }


        private void RefreshRuleView()
        {
            dgvRules.Rows.Clear();
            dgvRules.Columns.Clear();
            dgvRules.AutoGenerateColumns = false;

            int limit = _controller.K;

            if (limit > 300) limit = 300;

            //dgvRules.ColumnCount = _controller.K + 1;
            for (int i = 0; i <= limit; i++)
            {
                dgvRules.Columns.Add(new DataGridViewColumn
                {
                    HeaderText = "A" + i,
                    CellTemplate = new DataGridViewTextBoxCell()
                });
            }

            for (int i = 0; i <= _controller.K; i++)
            {
                dgvRules.Rows.Add(new DataGridViewRow
                {
                    HeaderCell = new DataGridViewRowHeaderCell
                    {
                        Value = "B" + i
                    },
                });
            }

            for (int y = 0; y <= _controller.K; y++)
            {
                for (int x = 0; x <= limit; x++)
                {
                    dgvRules.Rows[y].Cells[x] = new DataGridViewTextBoxCell
                    {
                        Tag = _controller.FuzzyItems[x, y],
                        Value = (_controller.FuzzyItems[x, y] == null) ? "NULL" : _controller.FuzzyItems[x, y].ToString()
                    };
                }
            }
        }

        private void button2_Click(object sender, EventArgs e)
        {
            SetParameters();
            int i = int.Parse(tbRandomCount.Text);

            Random rnd = new Random();
            for (; i >= 0; i--)
            {
                float x1 = (float)(_controller.StartAt + rnd.NextDouble() * (_controller.EndAt - _controller.StartAt));
                float x2 = (float)(_controller.StartAt + rnd.NextDouble() * (_controller.EndAt - _controller.StartAt));
                _controller.Learn(x1, x2);
            }

            RefreshRuleView();
        }

        private void button3_Click(object sender, EventArgs e)
        {
            SetParameters();
            float step = float.Parse(tbStep.Text);

            // Learn
            for (float x1 = _controller.StartAt; x1 <= _controller.EndAt; x1 = (float)Math.Round(x1+step,5))
            {
                for (float x2 = _controller.StartAt; x2 <= _controller.EndAt; x2 = (float)Math.Round(x2 + step, 5))
                {
                    _controller.Learn(x1, x2);
                }
            }
            RefreshRuleView();
        }

        private void button5_Click(object sender, EventArgs e)
        {
            if (folderBrowserDialog1.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                tbPath.Text = folderBrowserDialog1.SelectedPath;
            }
        }

        private void button4_Click(object sender, EventArgs e)
        {
            float step = float.Parse(tbSharpenStep.Text);
            FuzzyController.IntegralStep = double.Parse(integralStep.Text);
            // Unsharp

            // mean absolute deviation
            double mad = 0;
            // mean square deviation
            double msd = 0;
            // max deviation
            double md = 0;
            // ile punktów zostało wygenerowanych
            int count = 0;

            using (StreamWriter sw = new StreamWriter(File.Open(tbPath.Text + "out.dat", FileMode.Create, FileAccess.Write)))
            {
                using (StreamWriter dw = new StreamWriter(File.Open(tbPath.Text + "diff.dat", FileMode.Create, FileAccess.Write)))
                {
                    sw.WriteLine("#X Y Z");
                    dw.WriteLine("#X Y Z");
                    for (float x1 = _controller.StartAt; x1 <= _controller.EndAt; x1 += step)
                    {
                        x1 = (float)Math.Round(x1, 6);
                        for (float x2 = _controller.StartAt; x2 <= _controller.EndAt; x2 += step)
                        {
                            x2 = (float)Math.Round(x2, 6);
                            double? y = _controller.Sharpen(x1, x2);

                            // jeżeli jest null, to znaczy, że nie ma wartości
                            if (y != null)
                            {
                                count++;
                                sw.WriteLine("{0} {1} {2}", x1, x2, y);

                                y = Math.Abs(y.Value - _controller.Function(x1, x2));

                                mad += y.Value;
                                msd += y.Value * y.Value;
                                if (y.Value > md) md = y.Value;
                                dw.WriteLine("{0} {1} {2}", x1, x2, y);
                            }
                        }
                    }
                }
            }

            mad = mad / count;
            msd = mad / count;

            tbMSD.Text = msd.ToString();
            tbMAD.Text = mad.ToString();
            tbMD.Text = md.ToString();

            if (showGraph.Checked)
            {
                string gnuPlotFile = Path.GetTempFileName();
                string graphFile = Path.GetTempFileName();

                string assemblyPath = Path.Combine(tbPath.Text);

                using (StreamWriter sw = new StreamWriter(File.Open(gnuPlotFile, FileMode.Create, FileAccess.Write)))
                {
                    sw.WriteLine("set terminal jpeg xffffff");
                    sw.WriteLine("cd '" + assemblyPath + "'");
                    sw.WriteLine("set pointsize 1");
                    sw.WriteLine("set xlabel \"X1\"");
                    sw.WriteLine("set ylabel \"X2\"");
                    sw.WriteLine(@"set output '" + graphFile + "'");
                    sw.WriteLine("set title '" + FuzzyController.Gettitle(FuzzyController.SharpeningFunction) + "'");
                    if (onlyDiff.Checked)
                    {
                        sw.WriteLine("splot \"diff.dat\" ti \"Differences\" with points");
                    }
                    else
                    {
                        sw.WriteLine("splot \"out.dat\" ti \"Function\" with points");
                    }

                    if (epsFile.Checked)
                    {
                        //sw.WriteLine(@"set terminal postscript eps");
                        sw.WriteLine(@"set output 'graph.jpg'");
                        sw.WriteLine("replot");
                    }
                    sw.WriteLine("set output");
                }

                ProcessStartInfo psi = new ProcessStartInfo(Path.Combine(guplotPath.Text, "gnuplot.exe"), gnuPlotFile)
                {
                    CreateNoWindow = false,
                    WindowStyle = ProcessWindowStyle.Hidden
                };
                Process p = Process.Start(psi);

                if (p.WaitForExit(20000))
                {

                    if (p.ExitCode == 0)
                    {
                        File.Delete(gnuPlotFile);

                        GraphForm gf = new GraphForm();
                        gf.ShowFile(graphFile);
                    }
                    else
                    {
                        MessageBox.Show("GNUplot zakończył działanie kodem: " + p.ExitCode);
                    }
                }
                else
                {
                    MessageBox.Show("Przekroczono czas oczekiwania na wykres");
                }
                p.Dispose();
            }
        }

        private void sharpeningFunctions_SelectedValueChanged(object sender, EventArgs e)
        {
            FuzzyController.SharpeningFunction = sharpeningFunctions.SelectedIndex;
        }

        private void showResults_CheckedChanged(object sender, EventArgs e)
        {
            gnuPlotGroupBox.Enabled = showGraph.Checked;

            if (showGraph.Checked)
            {
                if (!File.Exists(Path.Combine(guplotPath.Text, "gnuplot.exe")))
                {
                    if (File.Exists(@"C:\Program Files\GnuPlot\gnuplot.exe"))
                    {
                        guplotPath.Text = @"C:\Program Files\GnuPlot";
                        gnuplotInfo.Text = "Automatycznie zmieniono ścieżkę z programem gnuplot.exe";
                    }
                    else
                    {
                        gnuplotInfo.Text = "Wskaż katalog, w którym znajduje się program gnuplot.exe";
                    }
                }
            }
        }

        private void button1_Click_1(object sender, EventArgs e)
        {
            if (folderBrowserDialog2.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                guplotPath.Text = folderBrowserDialog2.SelectedPath;
                gnuPlotGroupBox.Enabled = false;
                gnuPlotGroupBox.Enabled = true;
            }
        }

        private void SetParameters()
        {
            try
            {
                FuzzyController.OZFrom = double.Parse(tbStartY.Text);
                FuzzyController.OZTo = double.Parse(tbEndY.Text);
                int k = int.Parse(tbK.Text);

                _controller.K = k;
                RefreshRuleView();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "SetParameters");
            }
        }
    }

    public class FunctionItem
    {
        public Func<float, float, float> Function { get; set; }

        public string Name { get; set; }

        public float Start { get; set; }

        public float End { get; set; }

        public override string ToString()
        {
            return "[" + Start + "," + End + "]" + Name;
        }
    }
}
